use std::collections::HashMap;
use std::fs;

use serde::Serialize;

use crate::{components, dao};
use crate::models::models::components::v2ray::basic_setting::ComponentV2rayBasicSetting;
use crate::models::models::components::v2ray::dns_setting::ComponentV2rayDnsSetting;
use crate::models::models::components::v2ray::proxy_mode::ComponentV2rayProxyMode;
use crate::runtime::error::MagicBoxError;
use crate::runtime::response::Code;
use crate::runtime::result::MagicBoxResult;

pub(crate) fn generate_client_config() -> MagicBoxResult<()> {
    let config_path = components::v2ray::manager::manager().install_path()?.join("local_config.json");
    let mut config = V2rayClientConfig::default();

    let mut basic_setting = dao::components::v2ray::setting::get_basic_setting();
    if basic_setting.is_none() {
        basic_setting.replace(ComponentV2rayBasicSetting::default());
    }

    let basic_setting = basic_setting.unwrap();

    // SOCKS协议
    let mut socket_inbound = V2rayClientInbound::default();
    socket_inbound.tag = "socks".to_string();
    socket_inbound.listen = if basic_setting.local_area_network { "0.0.0.0".to_string() } else { "127.0.0.1".to_string() };
    socket_inbound.port = basic_setting.socket_port;
    socket_inbound.protocol = "socks".to_string();
    socket_inbound.settings.ip = Some(socket_inbound.listen.clone());
    socket_inbound.settings.auth = Some("noauth".to_string());
    socket_inbound.settings.udp = Some(basic_setting.udp);
    config.inbounds.push(socket_inbound);

    // HTTP协议
    let mut http_inbound = V2rayClientInbound::default();
    http_inbound.tag = "http".to_string();
    http_inbound.listen = if basic_setting.local_area_network { "0.0.0.0".to_string() } else { "127.0.0.1".to_string() };
    http_inbound.port = basic_setting.http_port;
    http_inbound.protocol = "http".to_string();
    http_inbound.settings.timeout = Some(0);
    config.inbounds.push(http_inbound);

    // DNS
    let mut dns_setting = dao::components::v2ray::setting::get_dns_setting();
    if dns_setting.is_none() {
        dns_setting.replace(ComponentV2rayDnsSetting::default());
    }

    let dns_setting = dns_setting.unwrap();
    config.dns.servers = dns_setting.servers.clone();

    // 路由
    let routers = dao::components::v2ray::router::get_all_routers()?;

    // OUTBOUNDS

    // 从servers表读取配置
    let servers = dao::components::v2ray::server::get_server_configs()?;
    for server in servers.iter() {
        if !basic_setting.enable_balancer && !server.used {
            continue;
        }

        let mut outbound = V2rayClientOutbound::default();
        outbound.send_through = "0.0.0.0".to_string();
        outbound.tag = server.tag.clone();
        outbound.protocol = "vmess".to_string();
        outbound.settings.vnext = Some(vec![V2rayClientOutboundSettingVnext::new(server.address.clone(), server.port.clone(), vec![V2rayClientOutboundSettingsUser::new(server.user_id.clone(), server.alter_id.clone(), server.level.clone(), server.security.clone())])]);

        let mut web_socket = V2rayClientOutboundStreamWebSocket::default();
        web_socket.path = server.websocket_setting.path.clone();
        web_socket.headers = serde_json::from_str::<HashMap<String, String>>(server.websocket_setting.headers_json_string.as_str()).or_else(|err| -> serde_json::Result<HashMap<String, String>> {
            log::error!("解析websocket headers失败: {}", err.to_string());

            let mut headers: HashMap<String, String> = HashMap::new();
            headers.insert("Host".to_string(), "".to_string());

            Ok(headers)
        }).unwrap();
        outbound.stream_settings = Some(V2rayClientOutboundStreamSettings::new(server.network.to_v2ray_network(), server.security.clone(), Some(web_socket)));

        if !basic_setting.enable_balancer {
            // 设置路由
            if ComponentV2rayProxyMode::Router == basic_setting.proxy_mode {
                let mut domains = Vec::<String>::new();
                let mut ips = Vec::<String>::new();

                for router in routers.iter() {
                    if "domain" == router._type { domains.push(router.domain_or_ip_rule.clone()); } else { ips.push(router.domain_or_ip_rule.clone()); }
                }

                config.routing.rules.push(V2rayClientConfigRoutingRule::new("field".to_string(), Some(domains), Some(ips), None, Some(server.tag.clone()), None));
            }

            if ComponentV2rayProxyMode::Global == basic_setting.proxy_mode || ComponentV2rayProxyMode::Manual == basic_setting.proxy_mode {
                config.routing.rules.push(V2rayClientConfigRoutingRule::new("field".to_string(), None, None, Some("0-65535".to_string()), Some(server.tag.clone()), None));
            }
        }

        config.outbounds.push(outbound);
    }

    if 0 == config.outbounds.len() {
        return Err(MagicBoxError::new(Code::UndefinedError, "未找到启用的服务器配置"));
    }

    config.outbounds.push(V2rayClientOutbound::new_direct_outbound());
    config.outbounds.push(V2rayClientOutbound::new_blocked_outbound());

    // ROUTING
    config.routing.domain_strategy = "IPOnDemand".to_string();
    config.routing.domain_matcher = "mph".to_string();

    if ComponentV2rayProxyMode::Manual != basic_setting.proxy_mode {
        config.routing.rules.push(V2rayClientConfigRoutingRule::new("field".to_string(), None, None, Some("0-65535".to_string()), Some("direct".to_string()), None));
    }

    // 负载均衡
    if basic_setting.enable_balancer {
        // 设置路由
        if ComponentV2rayProxyMode::Router == basic_setting.proxy_mode {
            let mut domains = Vec::<String>::new();
            let mut ips = Vec::<String>::new();

            for router in routers.iter() {
                if "domain" == router._type { domains.push(router.domain_or_ip_rule.clone()); } else { ips.push(router.domain_or_ip_rule.clone()); }
            }

            config.routing.rules.push(V2rayClientConfigRoutingRule::new("field".to_string(), Some(domains), Some(ips), None, None, Some("default".to_string())));
        }

        if ComponentV2rayProxyMode::Global == basic_setting.proxy_mode {
            config.routing.rules.push(V2rayClientConfigRoutingRule::new("field".to_string(), None, None, Some("0-65535".to_string()), None, Some("default".to_string())));
        }

        let mut tags: Vec<String> = Vec::new();
        for outbound in config.outbounds.iter() {
            if "direct" == outbound.tag.as_str() || "blocked" == outbound.tag.as_str() {
                continue;
            }

            tags.push(outbound.tag.clone());
        }

        // 添加负载均衡器
        config.routing.balancers.push(V2rayClientConfigRoutingBalancer::new("default".to_string(), tags.clone(), V2rayClientConfigRoutingBalancerStrategy { _type: "leastPing".to_string() }));

        // 设置连接观测
        config.observatory = Some(V2rayClientConfigObservatory::new(tags));
    }

    let bytes = serde_json::to_vec_pretty(&config);
    if bytes.is_err() {
        let err = bytes.unwrap_err().to_string();
        log::error!("序列化配置失败: {}", err.as_str());

        return Err(MagicBoxError::new(Code::IoError, format!("序列化配置失败: {}", err.as_str()).as_str()));
    }

    let bytes = bytes.unwrap();
    let result = fs::write(&config_path, &bytes);
    if result.is_err() {
        let err = result.unwrap_err().to_string();
        log::error!("写入配置失败: {}", err.as_str());

        return Err(MagicBoxError::new(Code::IoError, format!("写入配置失败: {}", err.as_str()).as_str()));
    }

    Ok(())
}

#[derive(Debug, Default, Clone, Serialize)]
struct V2rayClientInboundSettings {
    // 监听的IP地址，Socks协议有效
    #[serde(skip_serializing_if = "Option::is_none")]
    ip: Option<String>,
    // 鉴权方式，Socks协议有效
    #[serde(skip_serializing_if = "Option::is_none")]
    auth: Option<String>,
    // 是否允许UDP，Socks协议有效
    #[serde(skip_serializing_if = "Option::is_none")]
    udp: Option<bool>,
    // 超时时间，Http协议有效
    #[serde(skip_serializing_if = "Option::is_none")]
    timeout: Option<u64>,
}

#[derive(Debug, Default, Clone, Serialize)]
struct V2rayClientInbound {
    tag: String,
    listen: String,
    port: u16,
    protocol: String,
    settings: V2rayClientInboundSettings,
}

#[derive(Debug, Default, Clone, Serialize)]
struct V2rayClientOutboundSettingsUser {
    id: String,
    #[serde(rename = "alterId")]
    alter_id: i32,
    security: String,
    level: i32,
}

impl V2rayClientOutboundSettingsUser {
    pub(crate) fn new(id: String, alter_id: i32, level: i32, security: String) -> Self {
        Self {
            id,
            alter_id,
            level,
            security,
        }
    }
}

#[derive(Debug, Default, Clone, Serialize)]
struct V2rayClientOutboundSettingVnext {
    address: String,
    port: u16,
    users: Vec<V2rayClientOutboundSettingsUser>,
}

impl V2rayClientOutboundSettingVnext {
    pub(crate) fn new(address: String, port: u16, users: Vec<V2rayClientOutboundSettingsUser>) -> Self {
        Self {
            address,
            port,
            users,
        }
    }
}

#[derive(Debug, Default, Clone, Serialize)]
struct V2rayClientOutboundSettings {
    #[serde(skip_serializing_if = "Option::is_none")]
    vnext: Option<Vec<V2rayClientOutboundSettingVnext>>,
}

#[derive(Debug, Default, Clone, Serialize)]
struct V2rayClientOutboundStreamWebSocket {
    path: String,
    headers: HashMap<String, String>,
}

#[derive(Debug, Default, Clone, Serialize)]
struct V2rayClientOutboundStreamSettings {
    network: String,
    security: String,
    #[serde(rename = "wsSettings")]
    #[serde(skip_serializing_if = "Option::is_none")]
    ws_settings: Option<V2rayClientOutboundStreamWebSocket>,
}

impl V2rayClientOutboundStreamSettings {
    pub(crate) fn new(network: String, security: String, ws: Option<V2rayClientOutboundStreamWebSocket>) -> Self {
        Self {
            network,
            security,
            ws_settings: ws,
        }
    }
}

#[derive(Debug, Default, Clone, Serialize)]
struct V2rayClientOutbound {
    tag: String,
    #[serde(rename = "sendThrough")]
    send_through: String,
    protocol: String,
    settings: V2rayClientOutboundSettings,
    #[serde(rename = "streamSettings")]
    #[serde(skip_serializing_if = "Option::is_none")]
    stream_settings: Option<V2rayClientOutboundStreamSettings>,
}

impl V2rayClientOutbound {
    pub(crate) fn new_direct_outbound() -> Self {
        let mut outbound = V2rayClientOutbound::default();
        outbound.tag = "direct".to_string();
        outbound.send_through = "0.0.0.0".to_string();
        outbound.protocol = "freedom".to_string();

        outbound
    }

    pub(crate) fn new_blocked_outbound() -> Self {
        let mut outbound = V2rayClientOutbound::default();
        outbound.tag = "blocked".to_string();
        outbound.send_through = "0.0.0.0".to_string();
        outbound.protocol = "blackhole".to_string();

        outbound
    }
}

#[derive(Debug, Default, Clone, Serialize)]
struct V2rayClientConfigRoutingRule {
    #[serde(rename = "type")]
    _type: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    domains: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    ip: Option<Vec<String>>,
    #[serde(rename = "outboundTag")]
    #[serde(skip_serializing_if = "Option::is_none")]
    outbound_tag: Option<String>,
    #[serde(rename = "balancerTag")]
    #[serde(skip_serializing_if = "Option::is_none")]
    balancer_tag: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    port: Option<String>,
}

impl V2rayClientConfigRoutingRule {
    pub(crate) fn new(_type: String, domains: Option<Vec<String>>, ip: Option<Vec<String>>, port: Option<String>, outbound_tag: Option<String>, balancer_tag: Option<String>) -> Self {
        Self {
            _type,
            domains,
            ip,
            port,
            outbound_tag,
            balancer_tag,
        }
    }
}

#[derive(Debug, Default, Clone, Serialize)]
struct V2rayClientConfigRoutingBalancerStrategy {
    #[serde(rename = "type")]
    _type: String,
}

#[derive(Debug, Default, Clone, Serialize)]
struct V2rayClientConfigRoutingBalancer {
    tag: String,
    selector: Vec<String>,
    strategy: V2rayClientConfigRoutingBalancerStrategy,
}

impl V2rayClientConfigRoutingBalancer {
    pub(crate) fn new(tag: String, selector: Vec<String>, strategy: V2rayClientConfigRoutingBalancerStrategy) -> Self {
        Self {
            tag,
            selector,
            strategy,
        }
    }
}

#[derive(Debug, Default, Clone, Serialize)]
struct V2rayClientConfigRouting {
    #[serde(rename = "domainStrategy")]
    domain_strategy: String,
    #[serde(rename = "domainMatcher")]
    domain_matcher: String,
    rules: Vec<V2rayClientConfigRoutingRule>,
    balancers: Vec<V2rayClientConfigRoutingBalancer>,
}

#[derive(Debug, Default, Clone, Serialize)]
struct V2rayClientConfigDns {
    servers: Vec<String>,
}

#[derive(Debug, Default, Clone, Serialize)]
struct V2rayClientConfigObservatory {
    #[serde(rename = "subjectSelector")]
    subject_selector: Vec<String>,
    #[serde(rename = "probeInterval")]
    probe_interval: String,
    #[serde(rename = "probeURL")]
    probe_url: String,
}

impl V2rayClientConfigObservatory {
    pub(crate) fn new(subject_selector: Vec<String>) -> Self {
        Self {
            subject_selector,
            probe_interval: "1m".to_string(),
            probe_url: "https://api.v2fly.org".to_string(),
        }
    }
}

#[derive(Debug, Default, Clone, Serialize)]
struct V2rayClientConfig {
    inbounds: Vec<V2rayClientInbound>,
    outbounds: Vec<V2rayClientOutbound>,
    routing: V2rayClientConfigRouting,
    dns: V2rayClientConfigDns,
    #[serde(skip_serializing_if = "Option::is_none")]
    observatory: Option<V2rayClientConfigObservatory>,
}